// index.ts
import axios from 'axios';
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { MessagePlugin } from 'tdesign-vue-next';
import { useUserStore } from '@/store/modules/user';
import { LOGIN_URL } from '@/config/config';
import router from '@/router';

export type Result<T> = {
  code: number;
  message: string;
  data: T;
};

const config = {
  // 默认地址请求地址，可在 .env 开头文件中修改
  baseURL: import.meta.env.VITE_API_URL as string,
  // 设置超时时间（10s）
  timeout: 10000,
  // 跨域时候允许携带凭证
  withCredentials: true,
};

// 导出Request类，可以用来自定义传递配置来创建实例
class Request {
  // axios 实例
  instance: AxiosInstance;
  // 基础配置，url(可在 .env 开头文件中修改)、超时时间和跨域时候是否允许携带凭证
  //baseConfig: AxiosRequestConfig = { baseURL: import.meta.env.VITE_API_URL as string, timeout: 10000, withCredentials: true };

  constructor(config: AxiosRequestConfig) {
    // 使用axios.create创建axios实例
    this.instance = axios.create(config);

    this.instance.interceptors.request.use(
      // config: AxiosRequestConfig
      (config: any) => {
        // 一般会请求拦截里面加token，用于后端的验证
        const userStore = useUserStore();
        const token: string = userStore.token;
        return {
          ...config,
          headers: {
            ...config.headers,
            from: 'api',
            plat_flag: 'FORM',
            token: token,
          },
        };
      },
      (err: any) => {
        // 请求错误，这里可以用全局提示框进行提示
        return Promise.reject(err);
      },
    );

    /**
     * @description 响应拦截器
     *  服务器返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
     */
    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        const { data } = response;
        const userStore = useUserStore();
        // * 成功请求（在页面上除非特殊情况，否则不用处理失败逻辑）
        // * 登陆失效
        if ((data.code != 0 && data.msg === 'Token已过期') || data.code == 2) {
          MessagePlugin.error(data.msg);
          // 1.清除 Token
          userStore.SET_TOKEN('');
          // 2.重定向到登陆页
          router.replace(LOGIN_URL);
          return Promise.reject(data);
        }
        return data;
      },
      async (err: any) => {
        // 这里用来处理http常见错误，进行全局提示
        const { response } = err;
        // 请求超时单独判断，因为请求超时没有 response
        // 这里错误消息可以使用全局弹框展示出来
        if (err.message.indexOf('timeout') !== -1)
          MessagePlugin.error('请求超时！请您稍后重试');
        let message: string = '';
        if (response) {
          switch (err.response.status) {
            case 400:
              message = '请求错误(400)';
              break;
            case 401:
              message = '未授权，请重新登录(401)';
              // 这里可以做清空storage并跳转到登录页的操作
              break;
            case 403:
              message = '拒绝访问(403)';
              break;
            case 404:
              message = '请求出错(404)';
              break;
            case 408:
              message = '请求超时(408)';
              break;
            case 500:
              message = '服务器错误(500)';
              break;
            case 501:
              message = '服务未实现(501)';
              break;
            case 502:
              message = '网络错误(502)';
              break;
            case 503:
              message = '服务不可用(503)';
              break;
            case 504:
              message = '网络超时(504)';
              break;
            case 505:
              message = 'HTTP版本不受支持(505)';
              break;
            default:
              message = `连接出错(${err.response.status})!`;
          }
        }
        MessagePlugin.error(message);
        if (!window.navigator.onLine) router.replace('/500');
        // 这里是AxiosError类型，所以一般我们只reject我们需要的响应即可
        return Promise.reject(err);
      },
    );
  }

  // 定义请求方法
  request(config: AxiosRequestConfig): Promise<AxiosResponse> {
    return this.instance.request(config);
  }

  get<T>(url: string, params?: object): Promise<Result<T>> {
    return this.instance.get(url, { params });
  }

  post<T>(url: string, params?: object, _object?: object): Promise<Result<T>> {
    return this.instance.post(url, params, _object);
  }

  put<T>(url: string, params?: object): Promise<Result<T>> {
    return this.instance.put(url, params);
  }

  delete<T>(url: string, params?: object): Promise<Result<T>> {
    return this.instance.post(url, params);
  }
}

// 默认导出Request实例
export default new Request(config);
